#include "MapWidget.h"
#include "ui_MapWidget.h"
#include <QPainter>
#include <QStack>
#include "AStarThread.h"

MapWidget::MapWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MapWidget)
{
    ui->setupUi(this);
    nodeMapPtr = NULL;
    // TRY TO MAKE THIS SIZING DYNAMIC!!!
    pointsPixmap = QPixmap(850-2, 600-2);
    connectionsPixmap = QPixmap(850-2, 600-2);
    astarPixmap = QPixmap(850-2, 600-2);

    pointsPixmap.fill(Qt::transparent);
    connectionsPixmap.fill(Qt::transparent);
    astarPixmap.fill(Qt::transparent);
}

MapWidget::~MapWidget()
{
    if (nodeMapPtr != NULL)
    {
        for (NodeMap::iterator itr = nodeMapPtr->begin(); itr != nodeMapPtr->end(); ++itr)
        {
            delete itr.value();
        }
        delete nodeMapPtr;
    }
    delete ui;
}

void MapWidget::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);

    QRect box(0, 0, QWidget::width() - 1, QWidget::height() - 1);
    painter.drawRect(box);

    if (nodeMapPtr == NULL)
        return;

    painter.drawPixmap(1, 1, connectionsPixmap);
    painter.drawPixmap(1, 1, astarPixmap);
    painter.drawPixmap(1, 1, pointsPixmap);
}

void MapWidget::drawPoints(void)
{
    QPainter painter(&pointsPixmap);
    QBrush pointBrush(Qt::black);
    QPen textPen(Qt::white);
    painter.setBrush(pointBrush);
    painter.setPen(textPen);
    for (NodeMap::iterator itr = nodeMapPtr->begin(); itr != nodeMapPtr->end(); ++itr)
    {
        Node *nodePtr = itr.value();
        int x = nodePtr->getX();
        int y = nodePtr->getY();
        QString name = nodePtr->getName();
        painter.drawEllipse(QPoint(x, y), 12, 12);
        painter.drawText(x-7, y+3, name);
    }
    repaint();
}

void MapWidget::drawExploredPoint(Node *currentNode, int color)
{
    QPainter painter(&pointsPixmap);
    QBrush pointBrush(Qt::green);
    QPen textPen(Qt::black);
    painter.setPen(textPen);
    switch(color)
    {
    case 0:
    case 1:
        pointBrush.setColor(Qt::green);
        break;
    case 2:
        pointBrush.setColor(Qt::yellow);
        break;
    default:
        pointBrush.setColor(Qt::green);
        break;
    }
    QPoint p1(currentNode->getX(), currentNode->getY());
    painter.setBrush(pointBrush);
    painter.drawEllipse(p1, 12, 12);
    painter.drawText(p1.x()-7, p1.y()+3, currentNode->getName());
    repaint();
}

void MapWidget::drawConnections(void)
{
    QPainter painter(&connectionsPixmap);
    QPen linePen(Qt::red);
    linePen.setWidth(2);
    painter.setPen(linePen);
    for (NodeMap::iterator itr = nodeMapPtr->begin(); itr != nodeMapPtr->end(); ++itr)
    {
        int x1 = itr.value()->getX();
        int y1 = itr.value()->getY();
        std::vector<Node*> connections = itr.value()->getConnections();
        for (size_t i = 0; i < connections.size(); ++i)
        {
            int x2 = connections.at(i)->getX();
            int y2 = connections.at(i)->getY();
            painter.drawLine(x1, y1, x2, y2);
        }
    }
    repaint();
}

void MapWidget::buildMap(QString locsFile, QString connsFile)
{
    nodeMapPtr = Parser::parseData(locsFile, connsFile);
}

bool MapWidget::setPoints(QString start, QString destination, QString excludeMe)
{
    NodeMap::iterator itr = nodeMapPtr->find(start);
    startNodePtr = itr.value();

    itr = nodeMapPtr->find(destination);
    destinationNodePtr = itr.value();

    if (excludeMe.size() > 0)
    {
        if (excludeMe == start)
        {
            qDebug() << "Cannot exclude the starting point";
            return false;
        }

        else if (excludeMe == destination)
        {
            qDebug() << "Cannot exclude the destination";
            return false;
        }

        else
        {
            itr = nodeMapPtr->find(excludeMe);
            excludedNodePtr = itr.value();
            return true;
        }
    }
    else
    {
        excludedNodePtr = NULL;
        return true;
    }
}

void MapWidget::printNodes(void)
{
    for (NodeMap::iterator itr = nodeMapPtr->begin(); itr != nodeMapPtr->end(); ++itr)
    {
        itr.value()->print();
    }
    qDebug() << "\n" << "Number of nodes: " << nodeMapPtr->size();
}

QStringList MapWidget::getNodeNames(void)
{
    QStringList nameList;
    for (NodeMap::iterator itr = nodeMapPtr->begin(); itr != nodeMapPtr->end(); ++itr)
    {
        nameList.append(itr.key());
    }
    return nameList;
}

void MapWidget::printPathHome(void)
{
    QString path = "";
    QStack<QString> pathHome;

    for (Node *tmpNodePtr = destinationNodePtr; tmpNodePtr != NULL; tmpNodePtr = tmpNodePtr->getParent())
    {
        pathHome.push(tmpNodePtr->getName());
    }
    while (!pathHome.empty())
    {
        path.append(pathHome.top());
        pathHome.pop();
        if (pathHome.size() > 0)
        {
            path.append(" -> ");
        }
    }
    emit displayPath(path);
}

void MapWidget::drawPathToStart(Node *currentNode)
{
    QPainter painter(&astarPixmap);
    QPen connectionPen(Qt::green);
    connectionPen.setWidth(2);
    painter.setPen(connectionPen);
    astarPixmap.fill(Qt::transparent);

    for (Node *parent = currentNode->getParent(); parent != NULL;
         currentNode = parent, parent = parent->getParent())
    {
        QPoint p1(currentNode->getX(), currentNode->getY());
        QPoint p2(parent->getX(), parent->getY());
        painter.drawLine(p1, p2);
    }
    repaint();
}

void MapWidget::newNode(Node *newNode, int type)
{
    QString msg;
    drawExploredPoint(newNode, type);
    if(type == 0)
    {
        drawPathToStart(newNode);
        if(newNode->getName() != destinationNodePtr->getName())
            msg = "Exploring " + newNode->getName() + "\n";
    }
    else if (type == 2)
    {
        msg = "Found " + newNode->getName() + "\n";
        msg.append("F value: " + QString::number(newNode->getF()) + "\n");
    }
    emit sendMessage(msg);
}

void MapWidget::searchFinished(bool status)
{
    searchFinishedStatus = status;
}

void MapWidget::threadReturned(void)
{
    if (searchFinishedStatus)
    {
        printPathHome();
        QString msg = "Found destination ";
        msg += destinationNodePtr->getName();
        emit sendMessage(msg);
    }
    else
    {
        qDebug() << "\nPath from " << startNodePtr->getName() << " to " << destinationNodePtr->getName() <<
            " does not exist.";
    }
}

void MapWidget::clearMap(void)
{
    NodeMap::iterator itr = nodeMapPtr->begin();
    for (; itr != nodeMapPtr->end(); ++itr)
    {
        itr.value()->setParent(NULL);
        itr.value()->inOpenList(false);
        itr.value()->inClosedList(false);
    }
    pointsPixmap.fill(Qt::transparent);
    connectionsPixmap.fill(Qt::transparent);
    astarPixmap.fill(Qt::transparent);
    drawPoints();
    drawConnections();
}

void MapWidget::runAStar(void)
{
    AStarThread *worker = new AStarThread();
    worker->setNodes(startNodePtr, destinationNodePtr, excludedNodePtr);
    connect(worker, SIGNAL(exploredNewNode(Node*, int)), this, SLOT(newNode(Node*, int)));
    connect(worker, SIGNAL(finished(bool)), this, SLOT(searchFinished(bool)));
    connect(worker, SIGNAL(finished()), this, SLOT(threadReturned()));
    connect(this, SIGNAL(pause(void)), worker, SLOT(pauseSearch(void)));
    worker->start();
}

void MapWidget::pauseThread(void)
{
    emit pause();
}
